/*!	 valuenode_bonelink.cpp
**	 Implementation of the "BoneLink" valuenode conversion.
**
**	......... ... 2013 Ivan Mahonin
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "valuenode_bonelink.h"
#include "valuenode_const.h"
#include "valuenode_bone.h"
#include <synfig/general.h>
#include <synfig/localization.h>
#include <synfig/valuenode_registry.h>
#include <synfig/valueoperations.h>

#endif

using namespace std;
using namespace etl;
using namespace synfig;

REGISTER_VALUENODE(ValueNode_BoneLink, RELEASE_VERSION_1_0, "bone_link", "Bone Link")

ValueNode_BoneLink::ValueNode_BoneLink(const ValueBase &x):
    LinkableValueNode(x.get_type())
{
    Vocab ret(get_children_vocab());
    set_children_vocab(ret);

    set_link("bone",			ValueNode_Const::create(ValueNode_Bone::get_root_bone()));
    set_link("base_value",		ValueNode_Const::create(x));
    set_link("translate",		ValueNode_Const::create(true));
    set_link("rotate",  		ValueNode_Const::create(true));
    set_link("skew", 	 		ValueNode_Const::create(true));
    set_link("scale_x", 		ValueNode_Const::create(true));
    set_link("scale_y", 		ValueNode_Const::create(true));
}

ValueNode_BoneLink*
ValueNode_BoneLink::create(const ValueBase &x)
{
    return new ValueNode_BoneLink(x);
}

LinkableValueNode*
ValueNode_BoneLink::create_new()const
{
    return new ValueNode_BoneLink(get_type());
}

ValueNode_BoneLink::~ValueNode_BoneLink()
{
    unlink_all();
}

bool
ValueNode_BoneLink::set_link_vfunc(int i, ValueNode::Handle value)
{
    assert(i >= 0 && i < link_count());

    switch (i) {
    case 0:
        CHECK_TYPE_AND_SET_VALUE(bone_,      type_bone_valuenode);

    case 1:
        if (get_type() == type_nil) {
            VALUENODE_SET_VALUE(base_value_);
        } else {
            CHECK_TYPE_AND_SET_VALUE(base_value_, get_type());
        }

    case 2:
        CHECK_TYPE_AND_SET_VALUE(translate_, type_bool);

    case 3:
        CHECK_TYPE_AND_SET_VALUE(rotate_,    type_bool);

    case 4:
        CHECK_TYPE_AND_SET_VALUE(skew_,      type_bool);

    case 5:
        CHECK_TYPE_AND_SET_VALUE(scale_x_,   type_bool);

    case 6:
        CHECK_TYPE_AND_SET_VALUE(scale_y_,   type_bool);
    }

    return false;
}

ValueNode::LooseHandle
ValueNode_BoneLink::get_link_vfunc(int i)const
{
    assert(i >= 0 && i < link_count());

    switch (i) {
    case 0:
        return bone_;

    case 1:
        return base_value_;

    case 2:
        return translate_;

    case 3:
        return rotate_;

    case 4:
        return skew_;

    case 5:
        return scale_x_;

    case 6:
        return scale_y_;
    }

    return 0;
}

void
ValueNode_BoneLink::set_root_canvas(etl::loose_handle<Canvas> x)
{
    LinkableValueNode::set_root_canvas(x);
    bone_->set_root_canvas(x);
    base_value_->set_root_canvas(x);
    translate_->set_root_canvas(x);
    rotate_->set_root_canvas(x);
    skew_->set_root_canvas(x);
    scale_x_->set_root_canvas(x);
    scale_y_->set_root_canvas(x);
}

Transformation
ValueNode_BoneLink::get_bone_transformation(Time t)const
{
    Transformation transformation;
    ValueNode_Bone::Handle bone_node = (*bone_)(t).get(ValueNode_Bone::Handle());

    if (bone_node) {
        Bone bone      = (*bone_node)(t).get(Bone());
        bool translate = (*translate_)(t).get(true);
        bool rotate    = (*rotate_)(t).get(true);
        bool skew      = (*rotate_)(t).get(true);
        bool scale_x   = (*scale_x_)(t).get(true);
        bool scale_y   = (*scale_y_)(t).get(true);

        transformation.set_matrix(
            Matrix().set_scale(bone.get_local_scale())
            * bone.get_animated_matrix()
        );

        if (!translate) {
            transformation.offset = Vector(0.0, 0.0);
        }

        if (!rotate) {
            transformation.angle = Angle::zero();
        }

        if (!skew) {
            transformation.skew_angle = Angle::zero();
        }

        if (!scale_x) {
            transformation.scale[0] = 1.0;
        }

        if (!scale_y) {
            transformation.scale[1] = 1.0;
        }
    }

    return transformation;
}

ValueBase
ValueNode_BoneLink::operator()(Time t)const
{
    if (getenv("SYNFIG_DEBUG_VALUENODE_OPERATORS")) {
        printf("%s:%d operator()\n", __FILE__, __LINE__);
    }

    return ValueTransformation::transform(
               get_bone_transformation(t), (*base_value_)(t));
}

bool
ValueNode_BoneLink::check_type(Type &type)
{
    return ValueTransformation::check_type(type);
}

LinkableValueNode::Vocab
ValueNode_BoneLink::get_children_vocab_vfunc()const
{
    if (children_vocab.size()) {
        return children_vocab;
    }

    LinkableValueNode::Vocab ret;

    ret.push_back(ParamDesc(ValueBase(), "bone")
                  .set_local_name(_("Bone"))
                  .set_description(_("The linked bone"))
                 );

    ret.push_back(ParamDesc(ValueBase(), "base_value")
                  .set_local_name(_("Base value"))
                  .set_description(_("Base value"))
                 );

    ret.push_back(ParamDesc(ValueBase(), "translate")
                  .set_local_name(_("Translate"))
                  .set_description(_("Make translation"))
                 );

    ret.push_back(ParamDesc(ValueBase(), "rotate")
                  .set_local_name(_("Rotate"))
                  .set_description(_("Make rotation"))
                 );

    ret.push_back(ParamDesc(ValueBase(), "skew")
                  .set_local_name(_("Skew"))
                  .set_description(_("Make skew"))
                 );

    ret.push_back(ParamDesc(ValueBase(), "scale_x")
                  .set_local_name(_("Scale X"))
                  .set_description(_("Make scaling by X-axis"))
                 );

    ret.push_back(ParamDesc(ValueBase(), "scale_y")
                  .set_local_name(_("Scale Y"))
                  .set_description(_("Make scaling by Y-axis"))
                 );

    return ret;
}